# miaoma/main.py

import argparse
import logging
import sys
import yaml
from pathlib import Path
from typing import Dict, List, Optional, Tuple, Any

from src.core.command import Command, CommandRegistry
from src.commands.merge_config import MergeConfigCommand
from src.commands.parse_sql import ParseSQLCommand
from src.commands.preprocess_sql import PreprocessSQLCommand
from src.commands.gen_config import GenConfigCommand
from src.commands.gen_meta import GenMetaCommand
from src.commands.dump_table_meta import DumpTableMetaCommand
from src.commands.gen_code_check import GenCodeCheckCommand
from src.commands.analyze_template import AnalyzeTemplateConfigCommand
from src.commands.gen_code import GenCodeCommand
from src.commands.gen_project import GenProjectCommand
from src.base.registry import bera

TOOL_DESCRIPTION = """
妙码代码生成工具 (Miaoma Code Generator)

主要功能包括：
1. 标记解析
2. SQL文件预处理
3. SQL文件解析和处理
4. 配置文件生成
5. 元数据生成
6. 代码自动生成
"""

DEFAULT_CONFIG = {
    "verbose": False,
    "debug": False,
    "log_file": None,
    "output_dir": "output",
    "sql": {
        "dialect": "mysql",
        "encoding": "utf8",
        "case_sensitive": False
    },
    "code": {
        "template_dir": "templates",
        "force_overwrite": False,
        "backup": True
    }
}

class Config:
    """配置管理类"""

    def __init__(self):
        self._config = DEFAULT_CONFIG.copy()

    def load_file(self, file_path: str) -> None:
        """加载配置文件"""
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                file_config = yaml.safe_load(f)
                if file_config:
                    self._merge_config(self._config, file_config)
        except Exception as e:
            logging.warning(f"加载配置文件失败 {file_path}: {str(e)}")

    def load_default_configs(self) -> None:
        """加载默认配置文件"""
        # 按优先级从低到高加载配置
        config_files = [
            # 全局配置
            Path.home() / '.miaoma' / 'config.yaml',
            # 项目配置
            Path.cwd() / 'miaoma.yaml',
            # 本地配置
            Path.cwd() / 'miaoma.local.yaml'
        ]

        for config_file in config_files:
            if config_file.exists():
                self.load_file(str(config_file))

    def update_from_args(self, args: argparse.Namespace) -> None:
        """从命令行参数更新配置"""
        # 更新全局选项
        if args.verbose:
            self._config['verbose'] = True
        if args.debug:
            self._config['debug'] = True
        if args.log_file:
            self._config['log_file'] = args.log_file

        # 更新命令特定选项
        if hasattr(args, 'output'):
            self._config['output_dir'] = args.output

        if hasattr(args, 'dialect'):
            self._config['sql']['dialect'] = args.dialect

    def get(self, key: str, default: Any = None) -> Any:
        """获取配置值"""
        try:
            value = self._config
            for k in key.split('.'):
                value = value[k]
            return value
        except (KeyError, TypeError):
            return default

    def _merge_config(self, base: dict, update: dict) -> None:
        """递归合并配置"""
        for key, value in update.items():
            if key in base and isinstance(base[key], dict) and isinstance(value, dict):
                self._merge_config(base[key], value)
            else:
                base[key] = value

def create_parser(registry: CommandRegistry) -> argparse.ArgumentParser:
    """创建命令行解析器"""
    parser = argparse.ArgumentParser(
        description=TOOL_DESCRIPTION,
        formatter_class=argparse.RawDescriptionHelpFormatter
    )

    # 添加全局选项
    parser.add_argument('--verbose', '-v', action='store_true',
                       help='显示详细信息')
    parser.add_argument('--debug', '-D', action='store_true',
                       help='启用调试模式')
    parser.add_argument('--log-file', '-l',
                       help='日志文件路径')
    parser.add_argument('--config', '-c',
                       help='配置文件路径')

    # 添加命令参数(支持两种方式)
    parser.add_argument('command', nargs='?', help='要执行的命令')
    parser.add_argument('--command', dest='command_alt', help='要执行的命令(兼容格式)')

    # 创建子命令解析器
    subparsers = parser.add_subparsers(dest='subcommand')

    # 按分组添加命令
    for group_name, commands in sorted(registry.get_groups().items()):
        # 添加分组标题
        subparsers.add_parser(f'_{group_name}',
            help=f'=== {group_name} ===',
            formatter_class=argparse.RawDescriptionHelpFormatter)

        # 添加分组中的命令
        for cmd in sorted(commands, key=lambda x: x.name):
            help_text, epilog = cmd.get_help()
            cmd_parser = subparsers.add_parser(cmd.name,
                help=help_text,
                description=help_text,
                epilog=epilog,
                formatter_class=argparse.RawDescriptionHelpFormatter)
            cmd.add_arguments(cmd_parser)

    return parser

def setup_logging(args: argparse.Namespace) -> None:
    """设置日志"""
    log_format = '%(asctime)s [%(levelname)s] %(name)s: %(message)s'

    if args.log_file:
        logging.basicConfig(
            level=logging.DEBUG if args.debug else logging.INFO,
            format=log_format,
            filename=args.log_file
        )
        # 同时输出到控制台
        console = logging.StreamHandler()
        console.setLevel(logging.INFO)
        console.setFormatter(logging.Formatter(log_format))
        logging.getLogger('').addHandler(console)
    else:
        logging.basicConfig(
            level=logging.DEBUG if args.debug else logging.INFO,
            format=log_format
        )

def main():
    try:
        # 创建配置对象
        config = Config()

        # 加载默认配置
        config.load_default_configs()

        # 创建命令注册表
        registry = CommandRegistry()

        # 注册命令
        registry.register(PreprocessSQLCommand())  # SQL预处理命令
        registry.register(ParseSQLCommand())    # SQL解析命令
        registry.register(GenConfigCommand())   # 配置生成命令
        registry.register(MergeConfigCommand()) # 配置合并命令
        registry.register(GenMetaCommand())     # 元数据生成命令
        registry.register(DumpTableMetaCommand()) # 表元数据调试输出命令
        registry.register(GenCodeCheckCommand()) # 代码生成验证命令
        registry.register(AnalyzeTemplateConfigCommand()) # 模板配置分析命令
        registry.register(GenCodeCommand())     # 代码生成命令
        registry.register(GenProjectCommand())  # 项目生成命令

        # 创建解析器
        parser = create_parser(registry)

        # 解析参数
        args = parser.parse_args()

        # 处理命令参数兼容性
        if args.command_alt:
            args.command = args.command_alt

        # 如果使用子命令，则使用子命令名称
        if hasattr(args, 'subcommand') and args.subcommand and not args.subcommand.startswith('_'):
            args.command = args.subcommand

        if not args.command:
            parser.print_help()
            return

        # 加载指定的配置文件
        if args.config:
            config.load_file(args.config)

        # 从命令行参数更新配置
        config.update_from_args(args)

        # 将配置对象添加到参数中
        args.config_obj = config

        # 设置日志
        setup_logging(args)

        if args.command.startswith('_'):
            parser.print_help()
            return

        # 获取并执行命令
        command = registry.get_command(args.command)
        if command:
            try:
                command.pre_execute(args)
                command.execute(args)
                command.post_execute(args)
            except KeyboardInterrupt:
                bera.logger.warning("命令执行被中断")
                sys.exit(1)
            except Exception as e:
                logging.error(f"命令执行失败: {str(e)}", exc_info=True)
                sys.exit(1)
        else:
            bera.logger.error(f"未知命令: {args.command}")
            return 1

    except KeyboardInterrupt:
        bera.logger.warning("命令执行被中断")
        sys.exit(1)
    except Exception as e:
        if args.command not in commands:
            bera.logger.error(f"未知命令: {args.command}")
        raise

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        bera.logger.warning("程序执行被中断")
        sys.exit(1)
    except Exception as e:
        bera.logger.error(str(e))
        if os.environ.get("DEBUG"):
            raise
        sys.exit(1)